﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using System.Diagnostics;
using jaysJourney_v01.Common;

namespace jaysJourney_v01.Utils
{
    class Collider
    {
        /**
         * MEMBER VARIABLES
         * */

        // Parent

        private LevelBuilder curLevel;
        private int blockSize;
        Collidable collidable;

        // Calculations

        Point[] p; bool[] coll; Rectangle[] collR; Point[] CollisionResults;
        Rectangle curObject;

        /**
         * METHODS
         * */

        // Constructor

        public Collider(LevelBuilder level, int blockSize, Collidable col)
        {
            this.curLevel = level;
            this.blockSize = blockSize;
            this.collidable = col;
        }
        private int getCollisionType()
        {
            for (int x = 0; x < curLevel.XCOUNT; x++)
            {
                for (int y = 0; y < curLevel.YCOUNT; y++)
                {
                    // Skip Non-Collidable Blocks
                    if (curLevel.curLevel[x, y] == FieldType.Nothing) continue;
                    if (curLevel.curLevel[x, y] == FieldType.Startpoint) continue;
                    if (curLevel.curLevel[x, y] == FieldType.Goal) continue;

                    // Collision Rectangle for cur Block
                    Rectangle blockCollision = new Rectangle(x * this.blockSize, y * this.blockSize, this.blockSize, this.blockSize);
                    if (blockCollision.Intersects(curObject))
                    {
                        //Point s1 = new Point(blockCollision.Left,blockCollision.Top);
                        //Point s4 = new Point(blockCollision.Left, blockCollision.Bottom);
                        if (within(blockCollision, p[0])) { coll[0] = true; collR[0] = blockCollision; CollisionResults[0] = new Point(x, y); }
                        if (within(blockCollision, p[1])) { coll[1] = true; collR[1] = blockCollision; CollisionResults[1] = new Point(x, y); }
                        if (within(blockCollision, p[2])) { coll[2] = true; collR[2] = blockCollision; CollisionResults[2] = new Point(x, y); }
                        if (within(blockCollision, p[3])) { coll[3] = true; collR[3] = blockCollision; CollisionResults[3] = new Point(x, y); }
                        if (within(blockCollision, p[4])) { coll[4] = true; CollisionResults[4] = new Point(x, y); }
                        if (within(blockCollision, p[5])) { coll[5] = true; CollisionResults[5] = new Point(x, y); }

                        int r = checkForDirection(coll);
                        if (r > 0) return r;
                    }
                }
            }
            if (coll[4]) return 4;
            if (coll[5]) return 2;
            return 0;
        }
        private void UpdateVariables(int width, int height)
        {
            Vector2 colPos = collidable.getPosition();
            curObject = new Rectangle((int)colPos.X, (int)colPos.Y, width, height);
            p = new Point[6];
            p[0] = new Point(curObject.Left, curObject.Top);
            p[1] = new Point(curObject.Right, curObject.Top);
            p[2] = new Point(curObject.Right, curObject.Bottom);
            p[3] = new Point(curObject.Left, curObject.Bottom);
            p[4] = new Point(curObject.Left, curObject.Center.Y);
            p[5] = new Point(curObject.Right, curObject.Center.Y);

            coll = new bool[6];
            collR = new Rectangle[4];
        }
        private List<Point> prepareList()
        {
            List<Point> list = new List<Point>();
            for (int i = 0; i < 6; i++)
            {
                if (CollisionResults[i] != null&&CollisionResults[i].X>=0)
                {
                    bool isnew = true;
                    foreach (Point p in list)
                    {
                        if (p.Equals(CollisionResults[i])) isnew = false;
                    }
                    if (isnew) list.Add(CollisionResults[i]);
                }
            }
            /**foreach (Point p in list)
            {
                Console.Write(p.ToString());
            }
            Console.WriteLine();*/
            
            return list;
        }
        private void initiateArray()
        {
            CollisionResults = new Point[6];
            for (int i = 0; i < 6; i++)
            {
                CollisionResults[i] = new Point(-1, -1);
            }
        }
        // Check Horizontal Collision
        public List<Point> applyCollision(Collidable collidable, int width, int height)
        {
            this.collidable = collidable;
            Vector2 colPos = collidable.getPosition();
            curObject = new Rectangle((int)colPos.X, (int)colPos.Y, width, height);
            p = new Point[6];
            p[0] = new Point(curObject.Left, curObject.Top);
            p[1] = new Point(curObject.Right, curObject.Top);
            p[2] = new Point(curObject.Right, curObject.Bottom);
            p[3] = new Point(curObject.Left, curObject.Bottom);
            p[4] = new Point(curObject.Left, curObject.Center.Y);
            p[5] = new Point(curObject.Right, curObject.Center.Y);

            coll = new bool[6];
            collR = new Rectangle[4];
            initiateArray();
            int col = getCollisionType();
            
            //
            float runspeed = Math.Abs(collidable.getRunspeed());
            float fallspeed = Math.Abs(collidable.getFallspeed());
            Vector2 Position = collidable.getPosition();
            
            while (col > 0)
            {
                switch (col)
                {
                    case 2:
                        Position.X -= CONST.GRAVITY;
                        collidable.resetRunspeed();
                        break;
                    case 4:
                        Position.X += CONST.GRAVITY;
                        collidable.resetRunspeed();
                        break;
                    case 1:
                        Position.Y += CONST.GRAVITY;
                        collidable.resetFallspeed();
                        break;
                    case 3:
                        Position.Y -= CONST.GRAVITY;
                        collidable.resetFallspeed();
                        break;
                }
                collidable.setPosition(Position);
                UpdateVariables(width, height);
                col = getCollisionType();
            }
            float wRunspeed = collidable.getRunspeed();
            float wFallspeed = collidable.getFallspeed();
            Vector2 oldpos = collidable.getOldPosition();
            if (coll[0])
            {
                if (oldpos.X < collR[0].Right - 1)
                {
                    Position.Y = collR[0].Bottom;
                    if (collidable.getFallspeed() > 0) collidable.resetFallspeed();

                }
                else if (oldpos.Y <= collR[0].Bottom)
                {
                    Position.X = collR[0].Right - 1;
                    if (collidable.getRunspeed() < 0) collidable.resetRunspeed();
                }
                else if (p[0].Y + ((collR[0].Right - p[0].X) * ((oldpos.Y - p[0].Y ) / (oldpos.X - p[0].X))) < collR[0].Bottom)
                {
                    Position.X = collR[0].Right -  1;
                    if (collidable.getRunspeed() < 0) collidable.resetRunspeed();
                }
                else
                {
                    Position.Y = collR[0].Bottom;
                    if (collidable.getFallspeed() > 0) collidable.resetFallspeed();
                }
                collidable.setPosition(Position);
            }
            if (coll[1])
            {
                if (oldpos.X+width > collR[1].Left + 1)
                {
                    Position.Y = collR[1].Bottom;
                    if (collidable.getFallspeed() > 0) collidable.resetFallspeed();

                }
                else if (oldpos.Y <= collR[1].Bottom)
                {
                    Position.X = collR[1].Left -width + 1;
                    if (collidable.getRunspeed() > 0) collidable.resetRunspeed();
                }
                else if (p[1].Y + ((collR[1].Left - p[1].X) * ((p[1].Y - oldpos.Y )/( p[1].X - oldpos.X + width))) < collR[1].Bottom)
                {
                    Position.X = collR[1].Left - width + 1;
                    if (collidable.getRunspeed() > 0) collidable.resetRunspeed();
                }
                else
                {
                    Position.Y = collR[1].Bottom;
                    if (collidable.getFallspeed() > 0) collidable.resetFallspeed();
                }
                collidable.setPosition(Position);
            }
            if (coll[2])
            {
                if (oldpos.X + width > collR[2].Left + 1)
                {
                    Position.Y = collR[2].Top - height + 1;
                    if (collidable.getFallspeed() < 0) collidable.resetFallspeed();

                }
                else if (oldpos.Y + height >= collR[2].Top)
                {
                    Position.X = collR[2].Left - width + 1;
                    if (collidable.getRunspeed() > 0) collidable.resetRunspeed();
                }
                else if (p[2].Y - ((p[2].X - collR[2].Left) * ((p[2].Y - oldpos.Y + height) / (p[2].X - oldpos.X + width))) > collR[2].Top)
                {
                    Position.X = collR[2].Left - width + 1;
                    if (collidable.getRunspeed() > 0) collidable.resetRunspeed();
                }
                else
                {
                    Position.Y = collR[2].Top - height + 1;
                    if (collidable.getFallspeed() < 0) collidable.resetFallspeed();
                }
                collidable.setPosition(Position);
            }
            if (coll[3])
            {
                if (oldpos.X < collR[3].Right - 1)
                {
                    Position.Y = collR[3].Top - height + 1;
                    if (collidable.getFallspeed() < 0) collidable.resetFallspeed();

                }
                else if (oldpos.Y + height >= collR[3].Top)
                {
                    Position.X = collR[3].Right - 1;
                    if (collidable.getRunspeed() < 0) collidable.resetRunspeed();
                }
                else if (p[3].Y - ((collR[3].Right - p[3].X) * ((p[3].Y - oldpos.Y + height) / (oldpos.X - p[3].X))) > collR[3].Top)
                {
                    Position.X = collR[3].Right-1;
                    if (collidable.getRunspeed() < 0) collidable.resetRunspeed();
                }
                else
                {
                    Position.Y = collR[3].Top - height + 1;
                    if (collidable.getFallspeed() < 0) collidable.resetFallspeed();
                }
                collidable.setPosition(Position);
            }
            return prepareList();
        }

        private bool within(Rectangle r, Point p)
        {
            if (p.X >= r.Left && p.X <= r.Right && p.Y >= r.Top && p.Y <= r.Bottom) return true;
            return false;
        }
        private int checkForDirection(bool[] coll)
        {
            if (coll[0] && coll[1]) return 1;
            if ((coll[1] && coll[2]) || (coll[1] && coll[5]) || (coll[2] && coll[5])) return 2;
            if (coll[2] && coll[3]) return 3;
            if ((coll[3] && coll[0]) || (coll[3] && coll[4]) || (coll[0] && coll[4])) return 4;
            return 0;
        }
    }
}